/*
Copyright 2010  Murali Devi (profdevi@gmail.com)

This file is part of ComineGL Project.

ComineGL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

ComineGL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with ComineGL.  If not, see <http://www.gnu.org/licenses/>.

This code uses the irrlicht and irrklang libraries.  Please read
the libraries copyrights at 

	irrlicht library:  http://irrlicht.sourceforge.net
	irrklang library:  http://www.ambiera.com/irrklang

*/



//v0.15 copyright Comine.com 20120217S0519
#include <stdarg.h>
#include "MStdLib.h"
#include "Global.h"
#include "ComineGLAnimator.h"


//***********************************************
//** Module Elements
//***********************************************
struct GMoveToInfo
	{
	TRefCount<MCGLNodeInfo> *NodeInfo;
	};


//////////////////////////////////////////////////////
struct GRotateToInfo
	{
	TRefCount<MCGLNodeInfo> *NodeInfo;
	};


///////////////////////////////////////////////////////////
static bool GOnMoveToTask(MTimeEventQueue &manager,int eventid,void *extra)
	{
	GMoveToInfo *infodata=(GMoveToInfo *)extra;
	MStdAssert(infodata!=NULL);
	infodata->NodeInfo->ClearVelocity();
	return true;
	}


////////////////////////////////////////////////////////////
static bool GOnMoveToTaskEnd(MTimeEventQueue &manager,int eventid,void *extra)
	{
	GMoveToInfo *infodata=(GMoveToInfo *)extra;
	MStdAssert(infodata!=NULL);
	infodata->NodeInfo->Drop();
	infodata->NodeInfo=NULL;
	delete infodata;
	return true;
	}



///////////////////////////////////////////////////////////
static bool GOnRotateToTask(MTimeEventQueue &manager,int eventid,void *extra)
	{
	GRotateToInfo *infodata=(GRotateToInfo *)extra;
	MStdAssert(infodata!=NULL);
	infodata->NodeInfo->ClearRotation();
	return true;
	}


////////////////////////////////////////////////////////////
static bool GOnRotateToTaskEnd(MTimeEventQueue &manager,int eventid,void *extra)
	{
	GRotateToInfo *infodata=(GRotateToInfo *)extra;
	MStdAssert(infodata!=NULL);
	infodata->NodeInfo->Drop();
	infodata->NodeInfo=NULL;
	delete infodata;
	return true;
	}



////////////////////////////////////////////////////////////
static bool GRemoveAnimator(ISceneNode *node,ESCENE_NODE_ANIMATOR_TYPE animtype)
	{
	MStdAssert(node!=NULL);

	const ISceneNodeAnimatorList &itlist=node->getAnimators();
	ISceneNodeAnimatorList::ConstIterator i;

	ISceneNodeAnimatorList removelist;
	
	// Find All Animators that match animtype
	for(i=itlist.begin(); i!=itlist.end() ; ++i)
		{
		if( (*i)->getType()==animtype)
			{
			removelist.push_back(*i);
			}
		}

	// Now remove them from animator list
	for(i=removelist.begin();i!=removelist.end();++i)
		{
		node->removeAnimator(*i);
		}

	return true;
	}


//***********************************************
//** Exported Functions
//***********************************************
///////////////////////////////////////////////////////////
MDLLFUN bool CGLAnimatorClearAll(int nodeid)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}
	
	// Find Node
	MCGLNodeInfo *node=GlobalNodeList.Find(nodeid);
	if(node==NULL)
		{
		MStdPrintf("**Unable to find node with id=%d\n",nodeid);
		return false;
		}

	node->ClearAnimators();
	return true;	
	}


/////////////////////////////////////////////////
MDLLFUN bool CGLAnimatorRotation(int nodeid,float rx,float ry,float rz)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}
	
	// Find Node
	MCGLNodeInfo *node=GlobalNodeList.Find(nodeid);
	if(node==NULL)
		{
		MStdPrintf("**Unable to find node with id=%d\n",nodeid);
		return false;
		}

	if(node->SetRotation(rx,ry,rz)==false)
		{
		MStdPrintf("**Unable to set rotation to node %d\n",nodeid);
		return false;
		}

	return true;
	}


////////////////////////////////////////////////
MDLLFUN bool CGLAnimatorVelocity(int nodeid,float vx,float vy,float vz)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}
	
	// Find Node
	MCGLNodeInfo *node=GlobalNodeList.Find(nodeid);
	if(node==NULL)
		{
		MStdPrintf("**Unable to find node with id=%d\n",nodeid);
		return false;
		}

	if(node->SetVelocity(vx,vy,vz)==false)
		{
		MStdPrintf("**Unable to set velocity to node %d\n",nodeid);
		return false;
		}

	return true;
	}


////////////////////////////////////////////////
MDLLFUN bool CGLAnimatorMoveTo(int nodeid,float x,float y,float z,unsigned int timems)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}
	
	// Find Node
	TRefCount<MCGLNodeInfo> *node=GlobalNodeList.Find(nodeid);
	if(node==NULL)
		{
		MStdPrintf("**Unable to find node with id=%d\n",nodeid);
		return false;
		}

	ISceneNode *inode=node->GetNode();

	// Allocate dynamic memory for information
	GMoveToInfo *info=new GMoveToInfo;
	if(info==NULL)
		{
		return false;
		}

	// Init Node Information
	info->NodeInfo=node;
	node->Grab();

	// Now We Have to set up a call back to stop velocity at target positon
	GlobalInternalEventID = GlobalInternalEventID + 1;

	bool retval;
	retval=GlobalTaskQueueInternal.EventAdd(GlobalInternalEventID,false,timems
			,GOnMoveToTask,GOnMoveToTaskEnd,info);
	if(retval==false)
		{
		node->Drop();
		delete info;
		return false;
		}

	// Get Node Position
	vector3df position=inode->getPosition();
	vector3df target(x,y,z);

	// Calculate velocity
	vector3df velocity;
	velocity = (target-position)*(1.0f/timems);

	// Set up the velocity
	if(node->SetVelocity(float(velocity.X),float(velocity.Y),float(velocity.Z) )==false)
		{
		MStdPrintf("**Unable to set velocity to node %d\n",nodeid);
		node->Drop();
		delete info;
		return false;
		}
	
	return true;
	}



///////////////////////////////////////////////////////////////////
MDLLFUN bool CGLAnimatorRotateTo(int nodeid,float rx,float ry,float rz,unsigned int timems)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}
	
	// Find Node
	TRefCount<MCGLNodeInfo> *node=GlobalNodeList.Find(nodeid);
	if(node==NULL)
		{
		MStdPrintf("**Unable to find node with id=%d\n",nodeid);
		return false;
		}

	ISceneNode *inode=node->GetNode();

	// Allocate dynamic memory for information
	GRotateToInfo *info=new GRotateToInfo;
	if(info==NULL)
		{
		return false;
		}

	// Init Node Information
	info->NodeInfo=node;
	node->Grab();

	// Now We Have to set up a call back to stop velocity at target positon
	GlobalInternalEventID = GlobalInternalEventID + 1;

	bool retval;
	retval=GlobalTaskQueueInternal.EventAdd(GlobalInternalEventID,false,timems
			,GOnRotateToTask,GOnRotateToTaskEnd,info);
	if(retval==false)
		{
		node->Drop();
		delete info;
		return false;
		}

	// Get Node Position
	vector3df position=inode->getRotation();
	vector3df target(rx,ry,rz);

	// Calculate velocity
	vector3df velocity;
	velocity = (target-position)*(100.0f/timems);

	// Set up the velocity
	if(node->SetRotation(float(velocity.X),float(velocity.Y),float(velocity.Z) )==false)
		{
		MStdPrintf("**Unable to set rotational velocity for node %d\n",nodeid);
		node->Drop();
		delete info;
		return false;
		}
	
	return true;
	}



/////////////////////////////////////////////////////
MDLLFUN bool CGLAnimatorCollisionResponse(int nodeid,int trisetid,float radius,float gravity)
	{
	if(GlobalInitialized==false)
		{
		MStdPrintf("**Engine is not initialized\n");
		return false;
		}
	
	// Find Node
	MCGLNodeInfo *node=GlobalNodeList.Find(nodeid);
	if(node==NULL)
		{
		MStdPrintf("**Unable to find node with id=%d\n",nodeid);
		return false;
		}

	// Get iscenenode
	ISceneNode *inode=node->GetNode();

	// Find triset
	MCGLTriangleSelectorInfo *triset=GlobalTrisetList.Find(trisetid);
	if(triset==NULL)
		{
		MStdPrintf("**Unable to find triset %d\n",trisetid);
		return false;
		}

	ITriangleSelector *itriangleset=triset->GetSelector();
	
	ISceneNodeAnimator *animator=GlobalRefSceneMgr->createCollisionResponseAnimator(
			itriangleset,inode,vector3df(radius,radius,radius)
			,vector3df(0,gravity,0) );
	if(animator==NULL)
		{
		return false;
		}

	inode->addAnimator(animator);
	animator->drop();
	return true;
	}


